﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;

namespace Truncation.Detector.Windows
{
    public abstract class ProjectImpl
    {
        //private readonly ProjectInfo _projectInfo;
        private readonly Microsoft.Build.Evaluation.Project _project;
        private readonly string _nameSpace;
        private readonly string _assemblyName;
        private readonly List<Assembly> _referenceAssembly = new List<Assembly>();
        private readonly HashSet<ResourceData> _resourceReaders = new HashSet<ResourceData>();

        protected ProjectImpl(Microsoft.Build.Evaluation.Project project)
        {
            //_projectInfo = info;
            _project = project;
            _nameSpace = _project.GetPropertyValue("RootNamespace");
            _assemblyName = _project.GetPropertyValue("AssemblyName");
            ReadPureResxFiles();
            var targets = new List<string>();
            if (Version >= SolutionFormatVersion.VS2010)
            {
                targets.Add("ResolveReferences");
                targets.Add("DesignTimeResolveAssemblyReferences");
            }
            else
            {
                targets.Add("ResolveAssemblyReferences");
            }
            var instance = project.CreateProjectInstance();
            instance.SetProperty("BuildingProject", "false");
            instance.SetProperty("DesignTimeBuild", "true");
            instance.Build(targets.ToArray(), new[] { new ConsoleLogger(LoggerVerbosity.Minimal) });
            var resolvedAssemblyProjectItems = instance.GetItems("_ResolveAssemblyReferenceResolvedFiles");
            foreach (var resolvedAssemblyProjectItem in resolvedAssemblyProjectItems)
            {
                var ret = Assembly.LoadFile(resolvedAssemblyProjectItem.EvaluatedInclude);
                _referenceAssembly.Add(ret);
            }
        }

        public string FullPath { get { return _project.FullPath; } }
        public Microsoft.Build.Evaluation.Project MsBuildProject { get { return _project; } }
        public string NameSpace { get { return _nameSpace; } }
        public string AssemblyName { get { return _assemblyName; } }
        public abstract void Run(ISolutionImpl solution);
        public ICollection<Assembly> ReferenceAssemblies { get { return _referenceAssembly; } }

        public ResourceData GetResource(string key)
        {
            return _resourceReaders.FirstOrDefault(resourceReader => resourceReader.NameSpace == key);
        }

        private SolutionFormatVersion Version
        {
            get
            {
                if (string.IsNullOrEmpty(_project.ToolsVersion) || _project.ToolsVersion == "2.0")
                {
                    return SolutionFormatVersion.VS2005;
                }
                else if (_project.ToolsVersion == "3.0" || _project.ToolsVersion == "3.5")
                {
                    return SolutionFormatVersion.VS2008;
                }
                else
                {
                    return SolutionFormatVersion.VS2010;
                }
            }
        }

        private void ReadPureResxFiles()
        {
            var collection = _project.GetItems("EmbeddedResource");
            var resxSet = new HashSet<ResxFile>();
            var folder = Path.GetDirectoryName(_project.FullPath);
            if (folder == null)
                throw new Exception(String.Format("the path: {0} is not valid", _project.FullPath));
            foreach (var projectItem in collection)
            {
                var generator = projectItem.GetMetadataValue("Generator");
                var depend = projectItem.GetMetadataValue("DependentUpon");
                var resxFile = projectItem.EvaluatedInclude;
                if ((!String.IsNullOrEmpty(generator) && generator.EndsWith("CodeGenerator")) || 
                    (!String.IsNullOrEmpty(depend) && depend.EndsWith(".resx")) ||
                    (String.IsNullOrEmpty(generator) && String.IsNullOrEmpty(depend)))
                {
                    resxSet.Add(new ResxFile(folder, resxFile));
                }
            }
            var resxGroups = resxSet.GroupBy(s => s.FileName);
            foreach (var group in resxGroups)
            {
                var resx = group.AsEnumerable();
                _resourceReaders.Add(new ResourceData(resx));
            }
        }
    }

    public enum SolutionFormatVersion
    {
        VS2005 = 9,
        VS2008 = 10,
        VS2010 = 11,
        VS2012 = 12
    }

    public sealed class ProjectInfo
    {
        public string Title { get; set; }
        public string Location { get; set; }
        public Guid Guid { get; set; }
    }

    public class ResxFile
    {
        private readonly string _baseFolder;
        private readonly string _fileName;
        private readonly string _lang;

        public ResxFile(string baseFolder, string resxFile)
        {
            if (String.IsNullOrEmpty(resxFile) || !resxFile.EndsWith(".resx"))
                throw new Exception("the parameter of ResxFile() is wrong");
            this._baseFolder = baseFolder;
            _fileName = resxFile.Remove(resxFile.Length - 5);
            _lang = Path.GetExtension(_fileName);
            if (String.IsNullOrEmpty(_lang))
            {
                _fileName = resxFile;
                _lang = "";
            }
            else
            {
                _fileName = _fileName.Remove(_fileName.Length - _lang.Length) + ".resx";
                _lang = _lang.Substring(1);
            }
        }

        public string Folder { get { return _baseFolder; } }

        public string FileName { get { return _fileName; } }

        public string Lang { get { return _lang; } }
    }
}
